--- title: "01-封装" created: 2025-12-02 tags: - Java --- # 封装 **Java 的封装特性**是面向对象编程的核心原则之一,它指的是将对象的状态(数据)和行为(方法)封装在一个类内部,并通过公开的接口与外部进行交互。封装的主要目的是隐藏对象的内部实现细节,只暴露必要的功能,从而保护数据的完整性和减少系统的复杂性。 ## **扩展知识** ### **基本概念:** - **数据隐藏**:通过将类的字段(成员变量)声明为 `private` 或 `protected`,避免直接被外部访问。只有通过类提供的公共方法(如 getter 和 setter)才能访问和修改这些字段。 - **公共接口**:通过公共方法(如 getter 和 setter)提供访问对象数据的方式。这样可以对数据进行控制和验证,确保数据的一致性和合法性。 - **保护数据**:封装通过限制对数据的直接访问,减少了对对象状态的不安全修改和潜在的错误。 **示例代码**: ```java public class Person { // 私有字段 private String name; private int age; // 公共构造方法 public Person(String name, int age) { this.name = name; this.age = age; } // 公共 getter 方法 public String getName() { return name; } // 公共 setter 方法 public void setName(String name) { this.name = name; } // 公共 getter 方法 public int getAge() { return age; } // 公共 setter 方法 public void setAge(int age) { if (age > 0) { this.age = age; } else { throw new IllegalArgumentException("Age must be positive"); } } } ``` ## **扩展知识:** ### **封装的好处:** - **数据保护**:通过隐藏数据和提供受控的访问方法,可以防止外部代码对数据进行不合法的修改。 - **维护性**:封装使得对象的内部实现与外部接口分离,可以更容易地对内部实现进行更改,而不影响外部使用者。 - **简化接口**:提供简洁的公共接口,减少外部代码对类的复杂性理解,从而降低系统的耦合度。 - **代码复用**:通过封装,类可以重用已有的功能而不必重新实现,有助于构建模块化和可维护的代码。 ### **访问控制修饰符:** - `private`:只允许类内部访问,无法被外部访问。 - `protected`:允许同一包内的类以及子类访问。 - `public`:允许任何类访问。 - **默认(包级别)**:只允许同一包内的类访问。 ## 补充:封装的更高形态——不可变对象 getter/setter 只是封装的入门。封装更进一步,是**干脆不给 setter、不让状态可变**——创建之后对象完全不可修改(immutable): ```java public final class Money { private final long cents; // final 字段,只能在构造时赋值 private final String currency; public Money(long cents, String currency) { this.cents = cents; this.currency = currency; } public Money add(Money other) { // 不修改自己,返回新对象 return new Money(this.cents + other.cents, this.currency); } } ``` 不可变对象天然线程安全(没有可改的状态,就不会有并发修改问题),也可以放心地被缓存和共享——`String` 就是 JDK 里最著名的不可变类。`Integer`、`LocalDate` 同理。 > Java 16 引入的 `record` 是不可变数据载体的语法糖:`record Point(int x, int y) {}` 一行顶过去几十行,字段自动 `private final`,配套的构造器、访问器、equals/hashCode 全部自动生成。它就是"封装 + 不可变"思想的官方封装版。 ## 补充:什么时候 setter 也是坏味道 一个简单的自查标准:**setter 是否让对象在非法状态下存活**。比如: ```java Person p = new Person(); p.setName("张三"); // 此时 age 还没设,p 处于"半成品"状态,却已经能被使用 p.setAge(-5); // 非法状态悄悄通过 ``` 更好的做法是用构造器强制"诞生即合法",setter 只保留给确实需要中途变更的字段。封装的本质不是"生成一堆 getter/setter",而是**把"保证对象始终处于合法状态"的责任关在类内部**。 --- ⬅️ [[00-OOP特性|OOP 特性]] 🏠 [[00-Java|00-Java]] ➡️ [[02-引入接口与类之间的隔离|引入接口与类之间的隔离]]